
package com.epam.android.training.task;

import com.epam.android.training.task.rss.RssHandler;
import com.epam.android.training.task.rss.RssItem;

import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;

import android.app.ListActivity;
import android.content.Intent;
import android.graphics.Color;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.text.format.DateUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.text.ParseException;
import java.util.Comparator;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

/**
 * @author pavel_busko
 * 
 * RSS reader activity.
 */
public class NewsActivity extends ListActivity {

    final Comparator<RssItem> itemByDateComparator = new Comparator<RssItem>() {
        @Override
        public int compare(RssItem o1, RssItem o2) {
            if (o1.getDate() == null) {
                if (o2.getDate() == null) {
                    return 0;
                } else {
                    return 1;
                }
            } else if (o2.getDate() == null) {
                return -1;
            }
            return o2.getDate().compareTo(o1.getDate());
        };
    };

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        final LayoutInflater layoutInflater = getLayoutInflater();
        final ArrayAdapter<RssItem> adapter = createAdapter(layoutInflater);
        final View header = layoutInflater.inflate(R.layout.list_header, null);
        getListView().addHeaderView(header, null, false);
        setListAdapter(adapter);
        ListView lv = getListView();
        lv.setCacheColorHint(Color.WHITE);
        lv.setTextFilterEnabled(true);

        lv.setOnItemClickListener(new OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                Intent viewIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(adapter
                        .getItem(position).getLink().trim()));
                startActivity(viewIntent);
            }
        });

        createParsingTask(adapter, header).execute(getResources().getString(R.string.url_to_feed));
    }

    /**
     * ArrayAdapter definition extracted to separate method just to keep code
     * readable
     */
    private ArrayAdapter<RssItem> createAdapter(final LayoutInflater layoutInflater) {
        return new ArrayAdapter<RssItem>(getApplicationContext(), R.layout.list_item) {
            @Override
            public View getView(int position, View convertView, ViewGroup parent) {
                ViewHolder holder;
                if (convertView == null) {
                    convertView = layoutInflater.inflate(R.layout.list_item, parent, false);
                    holder = new ViewHolder(convertView);
                    convertView.setTag(holder);
                } else {
                    holder = (ViewHolder) convertView.getTag();
                }
                RssItem item = getItem(position);
                holder.setValues(item);
                return convertView;
            }
        };
    }

    /**
     * AsyncTask definition extracted to separate method just to keep code
     * readable
     */
    private AsyncTask<String, RssItem, String> createParsingTask(
            final ArrayAdapter<RssItem> adapter, final View header) {
        return new AsyncTask<String, RssItem, String>() {

            // to control if date parse error message was shown to user only
            // once
            private boolean dateParseErrorReported = false;

            @Override
            protected String doInBackground(String... params) {
                RssHandler handler = new RssHandler() {
                    @Override
                    public void onChannelInfoReady(RssItem item) {
                        publishProgress(item);
                    }

                    @Override
                    public void onItemReady(RssItem item) {
                        // we pass 'null' as a second argument just to
                        // distinguish this event from onChannelInfoReady()
                        publishProgress(item, null);
                    }

                    @Override
                    public void onException(ParseException e) {
                        // no arguments indicate exceptional case
                        publishProgress();
                    }

                };
                return parseWithHandler(handler, params[0]);
            }

            @Override
            protected void onProgressUpdate(RssItem... values) {
                if (values.length == 0) {
                    if (!dateParseErrorReported) {
                        // we show date parse error just once
                        Toast.makeText(getApplicationContext(),
                                getResources().getString(R.string.error_date_parse),
                                Toast.LENGTH_SHORT).show();
                        dateParseErrorReported = true;
                    }
                } else if (values.length == 1) {
                    ((TextView) header.findViewById(R.id.item_title)).setText(values[0].getTitle());
                    ((TextView) header.findViewById(R.id.item_detail)).setText(values[0]
                            .getDescription());
                } else {
                    adapter.add(values[0]);
                    adapter.sort(itemByDateComparator);
                }
            }

            @Override
            protected void onPostExecute(String error) {
                if (error != null) {
                    Toast.makeText(getApplicationContext(), error, Toast.LENGTH_SHORT).show();
                }
            }
        };
    }

    private String parseWithHandler(RssHandler handler, String urlString) {
        try {
            URL url = new URL(urlString);
            SAXParserFactory factory = SAXParserFactory.newInstance();
            SAXParser parser = factory.newSAXParser();
            XMLReader xmlreader = parser.getXMLReader();
            xmlreader.setContentHandler(handler);
            InputSource is = new InputSource(url.openStream());
            xmlreader.parse(is);
        } catch (MalformedURLException e) {
            return getResources().getString(R.string.error_incorrect_url) + urlString;
        } catch (IOException e) {
            e.printStackTrace();
            return getResources().getString(R.string.error_server_unavailable) + urlString;
        } catch (SAXException e) {
            return getResources().getString(R.string.error_parsing_failed);
        } catch (ParserConfigurationException e) {
            return getResources().getString(R.string.error_internal);
        }
        return null;
    }

    private class ViewHolder {

        TextView title;

        TextView detail;

        TextView date;

        ViewHolder(View itemView) {
            this.title = (TextView) itemView.findViewById(R.id.item_title);
            this.date = (TextView) itemView.findViewById(R.id.item_date);
            this.detail = (TextView) itemView.findViewById(R.id.item_detail);
        }

        void setValues(RssItem item) {
            title.setText(item.getTitle());
            date.setText(item.getDate() != null ? DateUtils.formatDateTime(getApplicationContext(),
                    item.getDate().getTime(), DateUtils.FORMAT_SHOW_DATE
                            + DateUtils.FORMAT_SHOW_TIME) : "");
            detail.setText(item.getDescription());
        }
    }
}
